package org.phalanx.server.game;

import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Random;

import org.phalanx.game.units.AbstractUnit;
import org.phalanx.game.units.VillageUnit;
import org.phalanx.messenging.MessageFeedListener;
import org.phalanx.server.ServerCache.CacheManager;
import org.phalanx.server.ServerCache.Cacheable;
import org.phalanx.server.user.PhalanxUser;

public class PhalanxGameManager {
	private CacheManager cacheMgr;

	public PhalanxGameManager(CacheManager cacheMgr){
		this.cacheMgr = cacheMgr;
	}

	public boolean createNewGame(PhalanxGameSettings newGame, Integer uId){
		boolean isSuccess = false;
		if (newGame.getGameName() != null && !isGameCreated(newGame.getGameName())){
			PhalanxUser u = (PhalanxUser)cacheMgr.getElement("UserCache", uId);

			if (u != null){
				PhalanxMapCacheWrapper gameMapWrap = (PhalanxMapCacheWrapper)cacheMgr.
				getElement("PhalanxMapCache", newGame.getMapName());

				if (gameMapWrap != null){
					newGame.setGameCreatorId(uId);
					newGame.setCurrentTurn(0);
					Integer generatedGameId = generateGameId();
					newGame.setId(generatedGameId);
					cacheMgr.setElement("GameListCache", generatedGameId , newGame);

//					PhalanxPlayerState pState = new PhalanxPlayerState(generatedGameId);
//
//					cacheMgr.setElement("PlayerStateCache", generatedGameId, pState);

					isSuccess = true;
				}
				else {
					// Let the user know its an invalid map
				}
			}
		}
		return isSuccess;
	}

	public boolean removeCurrentGame(Integer gameId, Integer uId){
		boolean isSuccess = false;

		if (isGameCreated(gameId)){

			// Change the game name to [GameName] and archive it
			PhalanxUser u = (PhalanxUser)cacheMgr.getElement("UserCache", uId);

			PhalanxGameSettings oldGame =(PhalanxGameSettings)cacheMgr.getElement("GameListCache", gameId);

			if (oldGame != null && oldGame.getGameCreatorId().equals(uId)){
				// Change the game name to [GameName] and archive it

				int count = 1;
				String archiveName;

				do {
					archiveName = "[" + oldGame.getGameName() + "]" + ((count == 1) ? "" : "(" + count + ")");
					count++;
				} while (this.getGameIdByName(archiveName) == null);

				oldGame.setGameName(archiveName);

				TimerCacheWrapper timer = (TimerCacheWrapper) cacheMgr.getElement("PlayerTurnTimer", gameId);

				if (timer != null){
					timer.getTimer().setPaused(true);

					cacheMgr.removeElement("PlayerTurnTimer", gameId);
				}

				cacheMgr.removeElement("GameEngineCache", gameId);
				cacheMgr.setElement("GameListCache", gameId, oldGame);

				// Don't delete the game or action states right away
				// Leave it for a daemon thread to garbage collect down the line
				isSuccess = true;
			}
		}

		return isSuccess;
	}

	public void pauseGame(Integer uId, Integer gameId, boolean pause){
		PhalanxGameSettings currentGame =(PhalanxGameSettings)cacheMgr.getElement("GameListCache", gameId);

		if (currentGame != null && currentGame.getGameCreatorId().equals(gameId)){
			TimerCacheWrapper timer = (TimerCacheWrapper)cacheMgr.getElement("PlayerTurnTimer", gameId);

			if (timer != null){
				timer.getTimer().setPaused(pause);
			}
		}
	}

	public ArrayList<Integer> getPlayersFromGame(Integer gId){
		ArrayList<Integer> playerIdList = new ArrayList<Integer>();

		PhalanxGameSettings gameSetting = (PhalanxGameSettings)cacheMgr.getElement("GameListCache", gId);
		Integer maxPlayers = gameSetting.getNumberOfPlayers();
		int foundPlayers = 0;


		if (isGameCreated(gId)){
			Enumeration<Object> e = cacheMgr.getCache("UserGameCache").getInternalCacheTable().keys();

			while (e.hasMoreElements() && foundPlayers != maxPlayers){
				Integer uId = (Integer)e.nextElement();

				GameMembers gMember = (GameMembers)cacheMgr.getElement("UserGameCache", uId);

				if (gMember.hasGameId(gId)){
					playerIdList.add(uId);
					foundPlayers++;
				}
			}
		}

		return playerIdList;
	}

	public boolean joinGame(Integer gameId, Integer uId, PhalanxGamePlayer playerX){
		boolean isSuccess = false;

		PhalanxGameSettings settings = (PhalanxGameSettings)cacheMgr.getElement("GameListCache", gameId);

		if (settings != null){
			PhalanxPlayerState pState = (PhalanxPlayerState)cacheMgr.getElement("PlayerStateCache", gameId);
			//			PhalanxUser u = (PhalanxUser)cacheMgr.getElement("UserCache", uId);

			if (!pState.hasPlayer(uId)){
				pState.addPlayerToList(playerX);

				GameMembers gMember = (GameMembers)cacheMgr.getElement("UserGameCache", uId);

				if (gMember == null){
					gMember = new GameMembers(uId);
				}

				gMember.addGameId(gameId);

				cacheMgr.setElement("UserGameCache", uId, gMember);
				cacheMgr.setElement("PlayerStateCache", gameId, pState);
				isSuccess = true;
			}

			// Write all this new data to the cache. New member is joined


		}

		return isSuccess;
	}

	public Integer getGameIdByName(String gameName){
		Integer gameId = -1;

		Enumeration<Cacheable> e = cacheMgr.getCache("GameListCache").getInternalCacheTable().elements();

		while (e.hasMoreElements() && gameId == -1){
			PhalanxGameSettings tempSetting = (PhalanxGameSettings)e.nextElement();

			if (tempSetting.getGameName().equalsIgnoreCase(gameName)){
				gameId = tempSetting.getId();
			}
		}

		return gameId;
	}

	public boolean isGameCreated(Integer gameId){
		Cacheable findGame = cacheMgr.getElement("GameListCache", gameId);

		return findGame != null;
	}

	public boolean isGameCreated(String gameName){
		return isGameCreated(getGameIdByName(gameName));
	}
	
	public boolean isGameStarted(Integer gameId){
		Cacheable findGameInProgress = cacheMgr.getElement("PlayerStateCache", gameId);
		
		return findGameInProgress != null;
	}

	public Integer generateGameId(){
		Integer gameId = -1;

		Random r = new Random();
		r.setSeed(new Date().getTime());

		do {
			gameId = Math.abs((r.nextInt() % 8999999) + 1000000);

		} while(isGameCreated(gameId));

		return gameId;
	}

	public ArrayList<PhalanxGameSettings> getGameList(String order, String criteria, String match, boolean includeArchived, Integer start){
		ArrayList<PhalanxGameSettings> matchList = new ArrayList<PhalanxGameSettings>();
		boolean criteriaMatch = false;

		Enumeration<Cacheable> e = cacheMgr.getCache("GameListCache").getInternalCacheTable().elements();

		while(e.hasMoreElements()){
			PhalanxGameSettings currentSettings = (PhalanxGameSettings)e.nextElement();
			boolean isArchived = isGameArchived(currentSettings);


			if (criteria == null){
				criteriaMatch = true;
			} else if (criteria == "name"){
				if (currentSettings.getGameName().equals(match)){
					criteriaMatch = true;
				}
			} else if (criteria == "creator"){
				Integer creatorId = Integer.valueOf(match);
				if (currentSettings.getGameCreatorId().equals(creatorId)){
					criteriaMatch = true;
				}
			} else if (criteria == "archived"){
				if (isArchived){
					criteriaMatch = true;
				}
			}

			if (criteriaMatch){
				if (isArchived && includeArchived || !isArchived && !includeArchived){
					criteriaMatch = false; // reset
					matchList.add(currentSettings);
				}
			}
		}

		ArrayList<PhalanxGameSettings> gList = new ArrayList<PhalanxGameSettings>();

		if (start < matchList.size()){
			int end = matchList.size() - start;
			if (end > 10){
				end = 10;
			}

			for (int i = start; i < start + end; i++){
				gList.add(matchList.get(i));
			}
		}

		return gList;
	}

	public boolean isGameArchived(PhalanxGameSettings gameSetting){
		boolean isArchived = false;
		String gameName = gameSetting.getGameName();

		if (gameName.startsWith("[")){
			isArchived = true;
		}

		return isArchived;
	}

	public boolean startNewGame(Integer gameId, Integer uId){
		boolean isSuccess = false;
		PhalanxGameSettings settings = (PhalanxGameSettings)cacheMgr.getElement("GameListCache", gameId);
		PhalanxPlayerState pState = (PhalanxPlayerState)cacheMgr.getElement("PlayerStateCache", gameId);

		if (settings != null && settings.getGameCreatorId().equals(uId) && settings.getNumberOfPlayers() > 1){
			GameState gState = new GameState(gameId);
			cacheMgr.setElement("CurrentGameState", gameId, gState);
			
			Iterator<PhalanxGamePlayer> i = pState.getPlayerList().iterator();
			
			while (i.hasNext()){
				PhalanxGamePlayer gPlayer = i.next();
				
				// Set villages on start point
				VillageUnit village = new VillageUnit();
				PhalanxUnitState uState = new PhalanxUnitState();
				uState.setTurnId(settings.getCurrentTurn());
				uState.setPhalanxUnit(village.getUnitName());
				uState.setHitPoints(village.getUnitAttribute(AbstractUnit.HIT_POINTS).getAttributeAsInteger());
				uState.setExpLevel(0);
				uState.setMapCoordinates(gPlayer.getBuildPoint());
				uState.setPosition(0);
				uState.setWealth(500);
				uState.setFormation(0);
			}

			PhalanxBattleRPGEngine gameEngine = (PhalanxBattleRPGEngine)cacheMgr.getElement("GameEngineCache", gameId);

			if (gameEngine != null){
				gameEngine.setInitialTurnConditions();

				TimerCacheWrapper pTimerWrapper = (TimerCacheWrapper)cacheMgr.getElement("PlayerTurnTimer", gameId);
				pTimerWrapper.getTimer().setPaused(false);
				isSuccess = true;
			}
		}

		return isSuccess;
	}

	public boolean setGameMove(Integer gameId, GameMove newMove){
		boolean isSuccess = false;
		Integer uId = newMove.getUserId();

		PhalanxGameSettings settings = (PhalanxGameSettings)cacheMgr.getElement("GameListCache", gameId);
		PhalanxBattleRPGEngine engine = (PhalanxBattleRPGEngine)cacheMgr.getElement("GameEngineCache", gameId);

		GameMoveQueue gQueue = null;

		if (settings != null && engine != null){
			Integer turnId = settings.getCurrentTurn();

			if (newMove.getTurn().equals(turnId) && this.getPlayersFromGame(gameId).contains(uId)){


				gQueue = (GameMoveQueue)cacheMgr.getElement("PlayerMoveCache", gameId);

				if (gQueue == null){
					gQueue = new GameMoveQueue(gameId);
				}

				if (engine.checkMoveSanity(newMove).equals(PhalanxBattleRPGEngine.MOVE_OK)){
					gQueue.addMove(newMove);

					// Add the moves to cache but don't write to DB yet

					cacheMgr.setElementNoDB("PlayerMoveCache", gameId, gQueue);
					isSuccess = true;
				}
			}
		}
		return isSuccess;
	}
	
	public void endGameTurnForPlayer(Integer gameId, Integer uId){
		PhalanxGameSettings settings = (PhalanxGameSettings)cacheMgr.getElement("GameListCache", gameId);
		TimerCacheWrapper pTimer = (TimerCacheWrapper)cacheMgr.getElement("PlayerTurnTimer", gameId);
		PhalanxPlayerState pState = (PhalanxPlayerState)cacheMgr.getElement("PlayerStateCache", gameId);

		
		if (settings != null && pTimer != null && pState != null){
			if (pState.hasPlayer(uId) && (settings.getTurnMode().equals(PhalanxGameSettings.PURE_TURN_MODE)
					|| settings.getTurnMode().equals(PhalanxGameSettings.MIXED_MODE))){
				pTimer.getTimer().setTurnFinished(uId);
			}
		}
	}
	
	public void sendGameStateToUser(Integer gameId, Integer uId){
		TimerCacheWrapper pTimer = (TimerCacheWrapper)cacheMgr.getElement("PlayerTurnTimer", gameId);
		
		if (pTimer != null){
			pTimer.getTimer().sendGameFeedToUser(uId);
		}
	}

}
